View Javadoc
1   // ZipFile.java, created Thu Jul  4  4:50:04 2002 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.ClassLib.Common.java.util.zip;
5   
6   import java.util.Enumeration;
7   import java.util.Hashtable;
8   import java.io.RandomAccessFile;
9   import jwutil.util.Assert;
10  
11  /***
12   * ZipFile
13   *
14   * @author  John Whaley <jwhaley@alum.mit.edu>
15   * @version $Id: ZipFile.java 1941 2004-09-30 03:37:06Z joewhaley $
16   */
17  public abstract class ZipFile implements ZipConstants {
18      public static final boolean TRACE = false;
19  
20      // existing instance fields
21      private String name;
22  
23      // additional instance fields
24      private RandomAccessFile raf;
25      private Hashtable entries;
26      private long cenpos;
27      private long pos;
28  
29      private static void initIDs() {
30      }
31      /*
32      public static void bootstrap_init(java.util.zip.ZipFile dis, String name) throws java.io.IOException {
33          jq_Class _class = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType("Ljava/util/zip/ZipFile;");
34          jq_InstanceField _name = _class.getOrCreateInstanceField("name", "Ljava/lang/String;");
35          Reflection.putfield_A(dis, _name, name);
36          jq_InstanceField _raf = _class.getOrCreateInstanceField("raf", "Ljava/io/RandomAccessFile;");
37          RandomAccessFile raf = new RandomAccessFile(name, "r");
38          Reflection.putfield_A(dis, _raf, raf);
39          //this.readCEN();
40      }
41      */
42      public native void __init__(java.lang.String name)
43          throws java.io.IOException;
44      public ZipFile(java.lang.String name) throws java.io.IOException {
45          this.__init__(name);
46      }
47      public ZipFile(java.io.File file, int mode) throws java.io.IOException {
48          this(file.getPath());
49          // delete mode not yet supported.
50          Assert._assert(mode == java.util.zip.ZipFile.OPEN_READ);
51      }
52      public java.util.zip.ZipEntry getEntry(String name) {
53          if (TRACE)
54              System.out.println(this +": getting entry " + name);
55          Hashtable entries = this.entries;
56          if (entries == null) {
57              // not yet initialized.
58              return null;
59          }
60          return (java.util.zip.ZipEntry) entries.get(name);
61      }
62      public Enumeration entries() {
63          Hashtable entries = this.entries;
64          return entries.elements();
65      }
66      public int size() {
67          Hashtable entries = this.entries;
68          if (entries == null) {
69              // not yet initialized.
70              return 0;
71          }
72          if (TRACE)
73              System.out.println(this +": getting size = " + entries.size());
74          return entries.size();
75      }
76      public void close() throws java.io.IOException {
77          if (TRACE)
78              System.out.println(this +": closing file");
79          RandomAccessFile raf = this.raf;
80          if (raf != null) {
81              raf.close();
82              this.raf = null;
83          }
84          entries = null;
85      }
86      private java.io.InputStream getInputStream(java.lang.String name)
87          throws java.io.IOException {
88          java.lang.Object o = getEntry(name);
89          return getInputStream((ZipEntry) o);
90      }
91      public java.io.InputStream getInputStream(ZipEntry ze)
92          throws java.io.IOException {
93          if (ze == null)
94              return null;
95          java.io.InputStream in = new ZipFileInputStream(this, ze);
96          if (TRACE)
97              System.out.println(
98                  this +": getting input stream for " + ze + " = " + in);
99          switch (ze.getMethod()) {
100             case java.util.zip.ZipEntry.STORED :
101                 return in;
102             case java.util.zip.ZipEntry.DEFLATED :
103                 java.util.zip.Inflater inflater;
104                 inflater = this.getInflater();
105                 if (TRACE)
106                     System.out.println(this +": using inflater " + inflater);
107                 // Overridden InflaterInputStream to add a zero byte at the end of the stream.
108                 return new InflaterInputStreamWrapper(this, in, inflater);
109             default :
110                 throw new java.util.zip.ZipException(
111                     "invalid compression method");
112         }
113     }
114     private native java.util.zip.Inflater getInflater();
115     private native void releaseInflater(java.util.zip.Inflater inf);
116     void releaseInflater0(java.util.zip.Inflater inf) {
117         releaseInflater(inf);
118     }
119 
120     private static class ZipFileInputStream extends java.io.InputStream {
121         private ZipFile zf;
122         private ZipEntry ze;
123         private long pos;
124         private long count;
125 
126         public ZipFileInputStream(ZipFile zf, ZipEntry ze)
127             throws java.io.IOException {
128             this.zf = zf;
129             this.ze = ze;
130             this.readLOC();
131         }
132 
133         public int read(byte b[], int off, int len)
134             throws java.io.IOException {
135             long count = this.count;
136             if (TRACE)
137                 System.out.println(
138                     this +": reading off=" + off + " len=" + len);
139             if (count == 0) {
140                 return -1;
141             }
142             if (len > count) {
143                 len = (int) Math.min(count, Integer.MAX_VALUE);
144             }
145             long pos = this.pos;
146             ZipFile zf = this.zf;
147             len = zf.read(pos, b, off, len);
148             if (len == -1) {
149                 throw new java.util.zip.ZipException("premature EOF");
150             }
151             this.pos = pos + len;
152             this.count = count - len;
153             return len;
154         }
155 
156         public int read() throws java.io.IOException {
157             long count = this.count;
158             if (count == 0) {
159                 return -1;
160             }
161             ZipFile zf = this.zf;
162             long pos = this.pos;
163             if (TRACE)
164                 System.out.println(this +": reading pos=" + pos);
165             int n = zf.read(pos);
166             if (n == -1) {
167                 throw new java.util.zip.ZipException("premature EOF");
168             }
169             this.pos = pos + 1;
170             this.count = count - 1;
171             if (TRACE)
172                 System.out.println(this +": new pos=" + (pos + 1));
173             if (TRACE)
174                 System.out.println(this +": new count=" + (count - 1));
175             return n;
176         }
177 
178         public long skip(long n) {
179             long count = this.count;
180             if (n > count) {
181                 n = count;
182             }
183             if (TRACE)
184                 System.out.println(this +": skipping " + n);
185             long pos = this.pos;
186             this.pos = pos + n;
187             this.count = count - n;
188             if (TRACE)
189                 System.out.println(this +": new pos=" + (pos + n));
190             if (TRACE)
191                 System.out.println(this +": new count=" + (count - n));
192             return n;
193         }
194 
195         public int available() {
196             long count = this.count;
197             return (int) Math.min(count, Integer.MAX_VALUE);
198         }
199 
200         private void cleanup() {
201             // nothing to do.
202         }
203 
204         public void close() {
205             cleanup();
206         }
207 
208         private void readLOC() throws java.io.IOException {
209             // Read LOC header and check signature
210             byte locbuf[] = new byte[LOCHDR];
211             ZipFile zf = this.zf;
212             ZipEntry ze = this.ze;
213             long offset = ze.getOffset();
214             if (TRACE)
215                 System.out.println(this +": reading LOC, offset=" + offset);
216             zf.read(offset, locbuf, 0, LOCHDR);
217             if (ZipFile.get32(locbuf, 0) != LOCSIG) {
218                 throw new java.util.zip.ZipException(
219                     "invalid LOC header signature");
220             }
221             // Get length and position of entry data
222             long count = ze.getCompressedSize();
223             this.count = count;
224             if (TRACE)
225                 System.out.println(this +": count=" + count);
226             long pos =
227                 ze.getOffset()
228                     + LOCHDR
229                     + ZipFile.get16(locbuf, LOCNAM)
230                     + ZipFile.get16(locbuf, LOCEXT);
231             this.pos = pos;
232             if (TRACE)
233                 System.out.println(this +": pos=" + pos);
234             long cenpos = zf.cenpos;
235             if (TRACE)
236                 System.out.println(this +": cenpos=" + cenpos);
237             if (pos + count > cenpos) {
238                 throw new java.util.zip.ZipException(
239                     "invalid LOC header format");
240             }
241         }
242     }
243     private /*synchronized*/
244     int read(long pos, byte b[], int off, int len) throws java.io.IOException {
245         if (TRACE)
246             System.out.println(
247                 this
248                     + ": reading file pos="
249                     + pos
250                     + " off="
251                     + off
252                     + " len="
253                     + len);
254         RandomAccessFile raf = this.raf;
255         if (raf == null)
256             throw new java.io.IOException();
257         if (pos != this.pos) {
258             raf.seek(pos);
259         }
260         int n = raf.read(b, off, len);
261         if (TRACE)
262             System.out.println(this +": number read=" + n);
263         if (TRACE)
264             System.out.println(this +": current pos=" + (pos + n));
265         if (n > 0) {
266             this.pos = pos + n;
267         }
268         return n;
269     }
270     private /*synchronized*/
271     int read(long pos) throws java.io.IOException {
272         if (TRACE)
273             System.out.println(this +": read pos=" + pos);
274         RandomAccessFile raf = this.raf;
275         if (raf == null)
276             throw new java.io.IOException();
277         if (pos != this.pos) {
278             if (TRACE)
279                 System.out.println(this +": seeking to " + pos);
280             raf.seek(pos);
281         }
282         int n = raf.read();
283         if (TRACE)
284             System.out.println(this +": byte read=" + n);
285         if (n > 0) {
286             this.pos = pos + 1;
287         }
288         return n;
289     }
290 
291     private void readCEN() throws java.io.IOException {
292         if (TRACE)
293             System.out.println(this +": reading CEN...");
294         // Find and seek to beginning of END header
295         long endpos = this.findEND();
296         if (TRACE)
297             System.out.println(this +": endpos=" + endpos);
298         // Read END header and check signature
299         byte[] endbuf = new byte[ENDHDR];
300         RandomAccessFile raf = this.raf;
301         raf.readFully(endbuf);
302         if (get32(endbuf, 0) != ENDSIG) {
303             throw new java.util.zip.ZipException(
304                 "invalid END header signature");
305         }
306         // Get position and length of central directory
307         long cenpos = get32(endbuf, ENDOFF);
308         if (TRACE)
309             System.out.println(this +": cenpos=" + cenpos);
310         this.cenpos = cenpos;
311         int cenlen = (int) get32(endbuf, ENDSIZ);
312         if (TRACE)
313             System.out.println(this +": cenlen=" + cenlen);
314         if (cenpos + cenlen != endpos) {
315             throw new java.util.zip.ZipException("invalid END header format");
316         }
317         // Get total number of entries
318         int nent = get16(endbuf, ENDTOT);
319         if (TRACE)
320             System.out.println(this +": nent=" + nent);
321         if (nent * CENHDR > cenlen) {
322             throw new java.util.zip.ZipException("invalid END header format");
323         }
324         // Check number of drives
325         if (get16(endbuf, ENDSUB) != nent) {
326             throw new java.util.zip.ZipException(
327                 "cannot have more than one drive");
328         }
329         // Seek to first CEN record and read central directory
330         raf.seek(cenpos);
331         byte cenbuf[] = new byte[cenlen];
332         raf.readFully(cenbuf);
333         // Scan entries in central directory and build lookup table.
334         Hashtable entries = new Hashtable(nent);
335         this.entries = entries;
336         for (int off = 0; off < cenlen;) {
337             // Check CEN header signature
338             if (get32(cenbuf, off) != CENSIG) {
339                 throw new java.util.zip.ZipException(
340                     "invalid CEN header signature");
341             }
342             ZipEntry e = new ZipEntry();
343             int entrysize = e.load(cenbuf, off, cenpos, cenlen);
344             off += entrysize;
345             if (TRACE)
346                 System.out.println(
347                     this +": entrysize=" + entrysize + " offset=" + off);
348             // Add entry to the hash table of entries
349             String name = e.getName();
350             entries.put(name, e);
351         }
352         if (false) { // zip files can have duplicate entries, so we disable this check.
353             // Make sure we got the right number of entries
354             if (entries.size() != nent) {
355                 throw new java.util.zip.ZipException(
356                     "invalid CEN header format");
357             }
358         }
359     }
360 
361     private static final int INBUFSIZ = 64;
362 
363     private long findEND() throws java.io.IOException {
364         if (raf == null)
365             throw new java.io.IOException();
366         // Start searching backwards from end of file
367         long len = raf.length();
368         if (TRACE)
369             System.out.println(this +": findEND len=" + len);
370         raf.seek(len);
371         // Set limit on how far back we need to search. The END header
372         // must be located within the last 64K bytes of the raf.
373         long markpos = Math.max(0, len - 0xffff);
374         // Search backwards INBUFSIZ bytes at a time from end of file
375         // stopping when the END header signature has been found. Since
376         // the signature may straddle a buffer boundary, we need to stash
377         // the first 4-1 bytes of the previous record at the end of
378         // the current record so that the search may overlap.
379         byte buf[] = new byte[INBUFSIZ + 4];
380         long pos = 0L; // Reflection.getfield_L(dis, _pos);
381         for (pos = len; pos > markpos;) {
382             int n = Math.min((int) (pos - markpos), INBUFSIZ);
383             pos -= n;
384             raf.seek(pos);
385             raf.readFully(buf, 0, n);
386             while (--n > 0) {
387                 if (get32(buf, n) == ENDSIG) {
388                     // Could be END header, but we need to make sure that
389                     // the record extends to the end of the raf.
390                     long endpos = pos + n;
391                     if (len - endpos < ENDHDR) {
392                         continue;
393                     }
394                     raf.seek(endpos);
395                     byte endbuf[] = new byte[ENDHDR];
396                     raf.readFully(endbuf);
397                     int comlen = get16(endbuf, ENDCOM);
398                     if (TRACE)
399                         System.out.println(this +": findEND comlen=" + comlen);
400                     if (endpos + ENDHDR + comlen != len) {
401                         continue;
402                     }
403                     // This is definitely the END record, so position
404                     // the file pointer at the header and return.
405                     raf.seek(endpos);
406                     this.pos = endpos;
407                     if (TRACE)
408                         System.out.println(
409                             this +": findEND pos=endpos=" + endpos);
410                     return endpos;
411                 }
412             }
413         }
414         throw new java.util.zip.ZipException(
415             "not a ZIP file (END header not found)");
416     }
417 
418     /*
419      * Fetch unsigned 16-bit value from byte array at specified offset.
420      * The bytes are assumed to be in Intel (little-endian) byte order.
421      */
422     static final int get16(byte b[], int off) {
423         return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8);
424     }
425 
426     /*
427      * Fetch unsigned 32-bit value from byte array at specified offset.
428      * The bytes are assumed to be in Intel (little-endian) byte order.
429      */
430     static final long get32(byte b[], int off) {
431         return get16(b, off) | ((long) get16(b, off + 2) << 16);
432     }
433 
434     // native method that is not used by this implementation.
435     private static void freeEntry(long a, long b) {
436         Assert.UNREACHABLE();
437     }
438 }